﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Permissions;
using System.Runtime.ConstrainedExecution;
using System.Security;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace SkypeMonitorService
{
	[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
	[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
	internal class MySafeTokenHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
	{
		private MySafeTokenHandle()
			: base(true)
		{
		}
		[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
		override protected bool ReleaseHandle()
		{
			return WinApiNativeMethods.CloseHandle(handle);
		}
	}

	[SuppressUnmanagedCodeSecurity()]
	internal static class WinApiNativeMethods
	{
		#region P/Invoke
		internal const int LOGON32_LOGON_INTERACTIVE = unchecked((int)2);
		internal const int LOGON32_PROVIDER_DEFAULT = unchecked((int)0);

		[DllImport("advapi32.dll")]
		internal static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword,
			int dwLogonType,
			int dwLogonProvider,
			out MySafeTokenHandle phToken);

		[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		internal static extern int DuplicateToken(MySafeTokenHandle hToken, int impersonationLevel, out MySafeTokenHandle hNewToken);

		[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
		[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
		internal static extern bool CloseHandle(IntPtr handle);
		#endregion
	}

	/// <summary>
	/// Realize user impersonation posobility
	/// </summary>
	[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
	public sealed class ImpersonationUtil : IDisposable
	{
		public ImpersonationUtil() { }

		~ImpersonationUtil()
		{
			Dispose(false);
		}

		WindowsImpersonationContext impersonationContext = null;
		private bool disposed = false;
		/// <summary>
		/// Loging from specified user
		/// </summary>
		/// <param name="userName">User name </param>
		/// <param name="domain">Domain</param>
		/// <param name="password">Password</param>
		/// <returns>false - if not logged in </returns>
		public void ImpersonateUser(String userName, String domain, String password)
		{
			WindowsIdentity tempWindowsIdentity;
			MySafeTokenHandle token;
			MySafeTokenHandle tokenDuplicate;
			if (WinApiNativeMethods.LogonUserA(userName, domain, password, WinApiNativeMethods.LOGON32_LOGON_INTERACTIVE, WinApiNativeMethods.LOGON32_PROVIDER_DEFAULT, out token) != 0)
			{
				using (token)
				{
					if (WinApiNativeMethods.DuplicateToken(token, 2, out tokenDuplicate) != 0)
					{
						using (tokenDuplicate)
						{
							if (!tokenDuplicate.IsInvalid)
							{
								tempWindowsIdentity = new WindowsIdentity(tokenDuplicate.DangerousGetHandle());
								impersonationContext = tempWindowsIdentity.Impersonate();
								return;
							}
						}
					}
				}
			}
			else
				throw new Exception("LogonUser failed: " + Marshal.GetLastWin32Error().ToString());
		}

		/// <summary>
		/// While release resources revert previous user
		/// </summary>
		public void Dispose()
		{
			Dispose(true);
			GC.SuppressFinalize(this);
		}

		protected void Dispose(bool disposing)
		{
			if (!disposed)
			{
				disposed = true;

				if (impersonationContext != null)
				{
					impersonationContext.Undo();
					impersonationContext.Dispose();
				}
				if (disposing)
				{
					//free unmanaged resources
				}
			}
		}

	}
}
